#pragma once

#include "common.hpp"
#include "Log.hpp"
#include <vector>

#define DEFAULT_LISTEN_BACKLOG 16

//设计模式: 模板方法模式
class Socket
{
protected:
    virtual void create_socket() = 0;
    virtual void bind_socket(uint16_t port) = 0;
    virtual void listen_socket(int backlog) = 0;

public:
    virtual int get_socket_fd() const = 0;

    //为了适配多路转接select, 我这里返回文件描述符, 稍微破坏一下封装性
    virtual int accept_socket(Inet_Addr& client/*输出型参数*/) = 0;

    virtual void connect_socket(const Inet_Addr& server) = 0;

    //改写为非阻塞版 -- return 读到的个数 或者read的错误状态
    virtual int receive(std::string& out_str/*输出型参数*/) = 0;

    //改写为非阻塞版 -- return 写入内核缓冲区的字节个数 或者send的错误状态
    virtual int send(const std::string& in_str) = 0;

    virtual bool close() = 0;

    //server调用
    void create_listen_socket(uint16_t port, int backlog = DEFAULT_LISTEN_BACKLOG)
    {
        create_socket();
        bind_socket(port);
        listen_socket(backlog);
    }

    //client调用
    void create_connect_socket(uint16_t server_port, const std::string& server_ip)
    {
        create_socket();
        connect_socket(Inet_Addr(server_port, server_ip));
    }

};



class TCP_Socket : public Socket
{
protected:
    virtual void create_socket() override
    {
        _socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
        if(_socket_fd < 0)
        {
            LOG(wzx::log_level::FATAL) << "_socket_fd create fail!!!";
            exit(LISTEN_SOCKFD_CON_ERR);
        }
        LOG(wzx::log_level::INFO) << "_socket_fd construct successfully, _socket_fd: " << _socket_fd;
    }

    virtual void bind_socket(uint16_t port) override
    {
        Inet_Addr server(port);
        int ret_bind = ::bind(_socket_fd, (const sockaddr*)(&server.get_addr()), sizeof(server.get_addr()));
        if(ret_bind < 0)
        {
            LOG(wzx::log_level::FATAL) << "bind fail!!!";
            exit(BIND_ERR);
        }
        LOG(wzx::log_level::INFO) << "bind successfully...";
    }

    virtual void listen_socket(int backlog) override
    {
        int ret_listen = ::listen(_socket_fd, backlog);
        if(ret_listen < 0)
        {
            LOG(wzx::log_level::FATAL) << "listen fail!!!";
            exit(LISTEN_ERR);
        }
        LOG(wzx::log_level::INFO) << "listen successfully...";
    }


public:
    TCP_Socket(int socket_fd = -1/*默认无效套接字*/)
        :_socket_fd(socket_fd)
    {}

    ~TCP_Socket()
    {
        close();
    }

    virtual int get_socket_fd() const override
    {
        return _socket_fd;
    }


    //         //不返回普通的文件描述符, 而一个Socket对象的指针 的目的: 后续读写套接字时, 可以直接以面向对象的形式读写
    // virtual std::shared_ptr<Socket> accept_socket(Inet_Addr& client/*输出型参数*/) override
    // {
    //     struct sockaddr_in client_addr;
    //     socklen_t len = sizeof(client_addr);
    //     int socket_fd = ::accept(_socket_fd, (sockaddr*)(&client_addr), &len);
    //     if(socket_fd < 0)
    //     {
    //         LOG(wzx::log_level::WARNING) << "accept failed~~";
    //         return nullptr;//获取失败就继续获取别的(张三吆喝失败接着找下一个路人)
    //     }
    //     LOG(wzx::log_level::INFO) << "accept successfully... " << client.get_ip() << " " << client.get_port();

    //     client.set_using_addr(client_addr);
        
    //     //为什么不能传引用返回的原因[重要!!!!!!!!]: 
    //     //  temp_ptr是局部变量：虽然它管理的TCP_Socket在堆上, 但temp_ptr这个shared_ptr对象本身在栈上, 
    //     //  函数结束时，temp_ptr被销毁, 而外部的接收对象被赋予到了一个未定义的地址!!!!!!!!!!!!

    //     return std::make_shared<TCP_Socket>(socket_fd);//[new一个子类对象]!!!! 父类指针指向子类, 实现多态
    //     //等价于: 
    //     //std::shared_ptr<Socket> temp_ptr = std::make_shared<TCP_Socket>(socket_fd);
    //     //return temp_ptr; 

    // }


    //为了适配多路转接select, 我这里返回文件描述符, 稍微破坏一下封装性
#define ACCEPT_DONE -1
#define ACCEPT_CONTINUE -2
    virtual int accept_socket(Inet_Addr& client/*输出型参数*/) override
    {
        struct sockaddr_in client_addr;
        socklen_t len = sizeof(client_addr);
        int socket_fd = ::accept(_socket_fd, (sockaddr*)(&client_addr), &len);
        if(socket_fd < 0) //适配非阻塞读取, 这里要做判断
        {
            if(errno == EAGAIN || errno == EWOULDBLOCK/*两个写一个就行*/) //1. 底层没有新连接请求了
            {
                LOG(wzx::log_level::INFO) << "There are no new connection requests to accept.";
                return ACCEPT_DONE;
            }
            else if(errno == EINTR) //2. 进程被信号打断暂停了, accept实际上没出错
            {
                LOG(wzx::log_level::INFO) << "process was interrupted by a signal and paused.";
                return ACCEPT_CONTINUE;
            }
            else //3. accept真出错了
            {
                LOG(wzx::log_level::ERROR) << "accept fail!!!";
                exit(ACCEPT_ERR);
            }
            
        }
        LOG(wzx::log_level::INFO) << "accept successfully... " << client.get_ip() << " " << client.get_port();

        client.set_using_addr(client_addr);
        
        return socket_fd;
    }


    virtual void connect_socket(const Inet_Addr& server) override
    {
        int ret_connect = ::connect(_socket_fd, (const sockaddr*)(&server.get_addr()), sizeof(server.get_addr()));
        if(ret_connect == -1)
        {
            LOG(wzx::log_level::FATAL) << "connect failed!!!";
            exit(CONNECT_ERR);
        }
        LOG(wzx::log_level::INFO) << "connect successfully... ";
    }



    //改写为非阻塞读版本
#define RECEIVE_DONE -1
#define RECEIVE_CONTINUE -2
    // 流式读取，不关心读到的是什么
    virtual int receive(std::string& out_str/*输出型参数*/) override
    {
        //char out_buffer[4096*16]; -- 缓冲区比较大了, 就不在栈上开辟了
        std::vector<char> out_buffer(4096*16, char());
        //int in_count = ::read(_socket_fd, in_buffer, sizeof(in_buffer) - 1);
        int out_count = ::recv(_socket_fd, out_buffer.data()/*返回指向作为元素存储的底层数组的指针*/, out_buffer.size() - 1, 0/*读方式*/);//读方式设为0, 等价于read
        if(out_count > 0)
        {
            out_buffer[out_count] = '\0';
            out_str += std::string(out_buffer.data()); //故意拼接的
            return out_count;
        }
        else if(out_count == 0)//read返回值为0代表对端关闭连接了
        {
            LOG(wzx::log_level::INFO) << "opposite side quit...";
            return out_count;
        }
        else //out_count < 0 -- 再做判断
        {
            if(errno == EAGAIN || errno == EWOULDBLOCK/*两个写一个即可*/)//[明确]标识内核缓冲区为空!!!
            {
                LOG(wzx::log_level::INFO) << "There is no data left in the kernel buffer.";
                return RECEIVE_DONE;
            }
            else if(errno == EINTR)//进程被信号打断暂停了, read实际没出错, 缓冲区也有可能没读完
            {
                LOG(wzx::log_level::INFO) << "receive process was interrupted by a signal and paused.";
                return RECEIVE_CONTINUE;
            }
            else//read真的出错了
            {
                LOG(wzx::log_level::FATAL) << "read failed!!!";
                exit(READ_ERR); 
            }
        }
        
    }


    //改写为非阻塞读版本
#define SEND_DONE -1
#define SEND_CONTINUE -2    
    virtual int send(const std::string& in_str) override
    {
        //::read(_socket_fd, in_str.c_str(), in_str.size());
        int in_count = ::send(_socket_fd, in_str.c_str(), in_str.size(), 0);
        if(in_count > 0)
            return in_count;
        else if(in_count == 0)//send返回值为0代表对端关闭连接了
        {
            LOG(wzx::log_level::INFO) << "opposite side quit...";
            return in_count;
        }
        else //in_count < 0 -- 再做判断
        {
            if(errno == EAGAIN || errno == EWOULDBLOCK/*两个写一个即可*/)//[明确]标识内核缓冲区已满, 无法再写入!!!
            {
                LOG(wzx::log_level::INFO) << "The kernel buffer is full, no more data can be sent.";
                return SEND_DONE;
            }
            else if(errno == EINTR)//进程被信号打断暂停了, send实际没出错, 缓冲区也有可能没读完
            {
                LOG(wzx::log_level::INFO) << "send process was interrupted by a signal and paused.";
                return SEND_CONTINUE;
            }
            else//read真的出错了
            {
                LOG(wzx::log_level::FATAL) << "send failed!!!";
                exit(SEND_ERR); 
            }
        }
    }


    virtual bool close() override
    {
        if(_socket_fd < 0)
            return false;
        
        ::close(_socket_fd);
        _socket_fd = -1;
        return true;
    }



private:
    int _socket_fd; //listen_socket_fd or socket_fd
};




// class UDP_Socket : public Socket
// {
//     ......
// };